OpenAI Whisper Large V3 TurboモデルをFaster Whisperで試してみました

OpenAI Whisper Large V3 TurboモデルをFaster Whisperで試してみました

Clock Icon2024.10.18

AWS事業本部コンサルティング部の石川です。OpenAIが最近リリースしたWhisper Large V3 Turboは、従来のWhisper Large V3モデルを最適化したバージョンです。日常的に利用していることもあり、私のMacBook(M1)のDocker環境で、Large V3 Turboを試してみます。

https://x.com/_philschmid/status/1841000069949989085

Large V3 Turboモデルとは

Whisper Large V3 Turboは、OpenAIが開発した音声認識モデルの最新バージョンです。Whisper Large V3を最適化したモデルで、精度をわずかに犠牲にしつつ大幅な高速化を実現しています。デコーダー層を32から4に削減することで、モデルサイズを小さくし処理速度を向上させています。

性能は、従来のWhisper Large V3と比較して、約216倍のリアルタイム速度を実現しており、日本語の音声認識精度は、Whisper Large V2よりもわずかに向上しています。

https://github.com/openai/whisper/discussions/2363

fast-whisperを使用した検証

fast-whisperは、OpenAIのWhisperモデルをCTranslate2を使用して再実装したライブラリで、高速な推論を可能にします。今回の検証では、このfast-whisperを使用してWhisper Large V3 Turboモデルの文字起こし時間の計測と結果を比較しました。

https://github.com/SYSTRAN/faster-whisper

検証環境

前提

MacBook(M1)、10CPU、32GB

今回は、CPUのみ利用します。

% system_profiler SPHardwareDataType
Hardware:
    Hardware Overview:
      Model Name: MacBook Pro
      Model Identifier: MacBookPro18,4
      Chip: Apple M1 Max
      Total Number of Cores: 10 (8 performance and 2 efficiency)
      Memory: 32 GB
      :

podman version 5.2.4

Dockerを動作するPodman Machineのスペックは、6CPU、15.56GBのメモリです。ここはホストOSからどれだけのコンピュートリソースを割り当てるによります。

openai-whisper-large-v3-turbo-faster-whisper-2

DockerイメージのビルドやDockerコンテナの実行は、podmanを利用しています。podmanコマンドにdockerというシンボリックリンクを設定しているため、dockerをCLIで使っているのと殆ど変わりません。

Dockerイメージ

Dockerイメージは、以下のDockerfileで構築してください。

FROM python:3.11-slim

WORKDIR /workspace
RUN apt-get update
RUN pip install --upgrade pip
RUN pip install numpy==1.26.2 onnxruntime==1.16.3 faster-whisper srt

以下のようにイメージをビルドします。

% docker build -t l3turbo . --platform linux/arm64
STEP 1/5: FROM python:3.11-slim
STEP 2/5: WORKDIR /workspace
--> 92de4953932d
STEP 3/5: RUN apt-get update
Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]
Get:2 http://deb.debian.org/debian bookworm-updates InRelease [55.4 kB]
Get:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]
Get:4 http://deb.debian.org/debian bookworm/main arm64 Packages [8689 kB]
Get:5 http://deb.debian.org/debian bookworm-updates/main arm64 Packages [2468 B]
Get:6 http://deb.debian.org/debian-security bookworm-security/main arm64 Packages [186 kB]
Fetched 9131 kB in 1s (6434 kB/s)
Reading package lists...
:
:
COMMIT l3turbo
--> 5e8eb5df4149
Successfully tagged localhost/l3turbo:latest
5e8eb5df4149514c169dcc41d3ae3cdf2b500156a0e1c4c19a5ca195f8a61b5c

% docker image ls
REPOSITORY                TAG         IMAGE ID      CREATED         SIZE
localhost/l3turbo         latest      ecffa05cc4dd  41 seconds ago  642 MB
docker.io/library/python  3.11-slim   692282a38c50  5 weeks ago     161 MB

Dockerコンテナ

docker runして、Dockerコンテナを起動します。最新のイメージ(IMAGE ID)を指定して起動しています。その際に、今回実行するソースコードと動画ファイルは、Mac上のディレクトリをDockeコンテナにそれぞれマウントしています。

% image_id=`docker images localhost/l3turbo:latest | tail -n 1 | awk '{print $3}'`
% docker run -it -w /workspace -v "/Users/<homedir>/playground/l3turbo:/workspace" -v "/Users/<homedir>/Desktop/movie:/movie" $image_id bash
root@09b568dbf342:/workspace# 

実際に実行すると、モデル(model.bin)が大きいため、ダウンロードにちょっと待たされます。

% image_id=`docker images localhost/l3turbo:latest | tail -n 1 | awk '{print $3}'`; docker run -it -w /workspace -v "/Users/<homedir>/playground/l3turbo:/workspace" -v "/Users/<homedir>/Desktop/movie:/movie" $image_id bash
root@09b568dbf342:/workspace# ./l3turbo.py /movie/test.mov 
config.json: 100%|███████████████████████████████████████████████████████████| 2.39k/2.39k [00:00<00:00, 12.0MB/s]
preprocessor_config.json: 100%|███████████████████████████████████████████████████████████| 340/340 [00:00<00:00, 4.71MB/s]
vocabulary.json: 100%|███████████████████████████████████████████████████████████| 1.07M/1.07M [00:00<00:00, 1.45MB/s]
tokenizer.json: 100%|███████████████████████████████████████████████████████████| 2.48M/2.48M [00:01<00:00, 2.22MB/s]
model.bin: 100%|███████████████████████████████████████████████████████████| 3.09G/3.09G [01:37<00:00, 31.7MB/s]
Transcribe Completed.|███████████████████████████████████████████████████████████| 3.09G/3.09G [01:37<00:00, 30.8MB/s]
Detected language 'ja' with probability 0.998877

検証用のソースコード

MacのQuickTime Playerで収録した動画からmp3ファイルを取得、指定した秒で区切ったmp3ファイルを作成、それぞれに対して文字起こしをします。文字起こしたSRTフォーマットのファイルとフラットなテキストファイルを出力します。model.transcribe()は、遅延評価なので、実際にイテレーションしないとtranscribeしないため、SRTファイルを出力するまでの時間を計測の対象としました。

なお、Initalizeのセクションで、GPUありのコードなどもコメントに残しておきますので色々お試しください。

Large V3(large-v3)用のソースコード(l3.py)

WhisperModelインスタンスを作成する際にmodel_size に "large-v3" を指定しています。

#!/usr/bin/env python

import os
import sys
import time
import srt
from faster_whisper import WhisperModel

def create_srt_file(results, use_faster_whisper, file_name="transcribe.srt"):
    data = []
    with open(file_name, mode="w") as f:
        for index, _dict in enumerate(results):
            if use_faster_whisper:
                start_time = _dict.start  # start
                end_time = _dict.end  # end
                text = _dict.text  # text
            else:
                start_time = _dict["start"]
                end_time = _dict["end"]
                text = _dict["text"]

            data.append({
                "index": index + 1,
                "start": start_time,
                "end": end_time,
                "text": text})

            # 時、分、秒、ミリ秒に分割
            s_h, s_m, s_s = int(
                start_time // 3600), int((start_time % 3600) // 60), int(start_time % 60)
            e_h, e_m, e_s = int(end_time // 3600), int((end_time %
                                                        3600) // 60), int(end_time % 60)

            # ミリ秒を計算
            s_ms = int((start_time - int(start_time)) * 1000)
            e_ms = int((end_time - int(end_time)) * 1000)

            f.write(f"{index+1}\n")
            f.write(
                f"{s_h:02}:{s_m:02}:{s_s:02},{s_ms:03} --> {e_h:02}:{e_m:02}:{e_s:02},{e_ms:03}\n")
            f.write(f"{text}\n\n")
    return data

path_video = sys.argv[1]
transcribe_file = sys.argv[1].replace(".mp4", "").replace(".mov", "")

#
# Initalize
#

model_size = "large-v3"
# Run on GPU with FP16
# model = WhisperModel(model_size, device="cuda", compute_type="float16")

# or run on GPU with INT8
# model = WhisperModel(model_size, device="cuda", compute_type="int8_float16")

# or run on CPU with INT8
model = WhisperModel(model_size, device="cpu", compute_type="int8")

#
# Transcribe with VAD filter
#
start_time = time.time()
segments, info = model.transcribe(
    path_video, beam_size=5, vad_filter=True, vad_parameters=dict(min_silence_duration_ms=1000))
print("Transcribe Completed.")
print("Detected language '%s' with probability %f" %
      (info.language, info.language_probability))

#
# Create SRT File
#
faster_whisper_vad_filter_data = create_srt_file(
    results=segments, use_faster_whisper=True, file_name=f"{transcribe_file}.srt")

execution_time = time.time() - start_time
print(f'execution_time = {execution_time}')

#
# Create TEXT File
#
contents = []
with open(f"{transcribe_file}.srt", mode="r", encoding="utf-8") as f:
    subs = srt.parse(f.read())
    for sub in subs:
        contents.append(sub.content + "\n")
with open(f"{transcribe_file}.txt", "w") as f:
    f.write("".join(contents))

#
# Cleanup
#
# os.remove(path_video)

Large V3 Turbo(large-v3-turbo)用のソースコード

WhisperModelインスタンスを作成する際にmodel_size に "large-v3-turbo" を指定できませんでしたので、代わりにmodel_size_or_path="deepdml/faster-whisper-large-v3-turbo-ct2"と指定しました。

#!/usr/bin/env python

import os
import sys
import time
import srt
from faster_whisper import WhisperModel

def create_srt_file(results, use_faster_whisper, file_name="transcribe.srt"):
    data = []
    with open(file_name, mode="w") as f:
        for index, _dict in enumerate(results):
            if use_faster_whisper:
                start_time = _dict.start  # start
                end_time = _dict.end  # end
                text = _dict.text  # text
            else:
                start_time = _dict["start"]
                end_time = _dict["end"]
                text = _dict["text"]

            data.append({
                "index": index + 1,
                "start": start_time,
                "end": end_time,
                "text": text})

            # 時、分、秒、ミリ秒に分割
            s_h, s_m, s_s = int(
                start_time // 3600), int((start_time % 3600) // 60), int(start_time % 60)
            e_h, e_m, e_s = int(end_time // 3600), int((end_time %
                                                        3600) // 60), int(end_time % 60)

            # ミリ秒を計算
            s_ms = int((start_time - int(start_time)) * 1000)
            e_ms = int((end_time - int(end_time)) * 1000)

            f.write(f"{index+1}\n")
            f.write(
                f"{s_h:02}:{s_m:02}:{s_s:02},{s_ms:03} --> {e_h:02}:{e_m:02}:{e_s:02},{e_ms:03}\n")
            f.write(f"{text}\n\n")
    return data

path_video = sys.argv[1]
transcribe_file = sys.argv[1].replace(".mp4", "").replace(".mov", "")

#
# Initalize
#

# model_size = "large-v3"

# Run on GPU with FP16
# model = WhisperModel(model_size, device="cuda", compute_type="float16")

# or run on GPU with INT8
# model = WhisperModel(model_size, device="cuda", compute_type="int8_float16")

# or run on CPU with INT8
# model = WhisperModel(model_size, device="cpu", compute_type="int8")
model = WhisperModel(model_size_or_path="deepdml/faster-whisper-large-v3-turbo-ct2", device="cpu", compute_type="int8")

#
# Transcribe with VAD filter
#

start_time = time.time()
# segments, info = model.transcribe(path_video, beam_size=5, vad_filter=True, vad_parameters=dict(min_silence_duration_ms=300))
segments, info = model.transcribe(path_video, beam_size=5, vad_filter=True, vad_parameters=dict(min_silence_duration_ms=1000))
print("Transcribe Completed.")
print("Detected language '%s' with probability %f" %
      (info.language, info.language_probability))

#
# Create SRT File
#
faster_whisper_vad_filter_data = create_srt_file(
    results=segments, use_faster_whisper=True, file_name=f"{transcribe_file}.srt")

execution_time = time.time() - start_time
print(f'execution_time = {execution_time}')

#
# Create TEXT File
#
contents = []
with open(f"{transcribe_file}.srt", mode="r", encoding="utf-8") as f:
    subs = srt.parse(f.read())
    for sub in subs:
        contents.append(sub.content + "\n")
with open(f"{transcribe_file}.txt", "w") as f:
    f.write("".join(contents))

#
# Cleanup
#
# os.remove(path_video)

検証用の動画ファイル

検証用の動画は、権利関係があると面倒なので自分の登壇動画の冒頭の5分(300秒)を再生して、それをQuickTime Playerで収録しています。そのため、いい感じで音が割れたリアルな録音した音になっています。

性能評価

動画ファイルを使用して、Whisper Whisper Large V3 と Large V3 Turboの処理速度と文字起こし結果を比較しました。

Large V3(large-v3)の実行(l3.py)

large-v3の文字起こしの時間は、約297秒

root@82a57821379b:/workspace# ./l3.py /movie/test.mov 
preprocessor_config.json: 100%|
Transcribe Completed.                                                                                                         Detected language 'ja' with probability 
execution_time = 296.7839891910553

Large V3 Turbo(large-v3-turbo)の実行(l3turbo.py)

large-v3の文字起こしの時間は、約94秒

hisper Large V3 Turboは、Whisper Large V3と比較して約3.16倍高速に動作しました。

root@82a57821379b:/workspace# ./l3turbo.py /movie/test.mov 
Transcribe Completed.
Detected language 'ja' with probability 0.999178
execution_time = 93.80634045600891

文字起こし結果の比較

差分(diff)の結果を見る限り、平仮名や英語のカタカナ表記が多い傾向が見られますが、大きな文字抜けはありません。

% diff test-l3.txt test-l3turbo.txt
2c2,3
< 石川悟といいます このセッションでは データ分析を支える技術
---
> 石川さとるといいます
> このセッションでは データ分析を支える技術
4,5c5,7
< 簡単に自己紹介をさせていただきます 普段はデータ分析基盤の設計や開発
< コンサルの傍ら デベロッパーズIOで データ分析関連の技術ブログを書いています
---
> 簡単に自己紹介をさせていただきます
> 普段はデータ分析基盤の設計や開発 コンサルの傍ら
> Developers.ioでデータ分析関連の 技術ブログを書いています
8,12c10,15
< 昨年 AWSのアナウンスで Redshift Serverlessに関する
< 私のコメントが公開されました その過程で 弊社のCSA
< Customer Story Analytics Serviceの 開発チームの協力の下
< 接続検証も実施しております 既に東京リージョンで
< ご利用可能です Redshift Serverlessによる シンプルなエクスペリエンスが
---
> 昨年 AWSのアナウンスで Redshiftサーバレスに関する
> 私のコメントが公開されました
> その過程で 弊社のCSA カスタマーストーリーアナリティクスサービスの
> 開発チームの協力の下 接続検証も実施しております
> すでに東京リージョンでご利用可能です
> Redshiftサーバレスによるシンプルなエクスペリエンスが
14,42c17,48
< 本セッションは 下期のセッションの 続編となりますので
< 併せてご覧いただくことをお勧めします 分かりそうで分からない
< 本日のテーマは DWHのデータモデリングです
< DWHのデータモデリングとは何か 代表的なデータモデリング手法の紹介と
< 長所 短所 選択のポイントについて 解説します
< ここでは DWHのデータモデリングの 概要と目的について解説します
< DWHとは何かを理解し DWHの目的を 確認したいと思います
< データ分析用途のDBである DWHデータウェアハウスに
< データを分かりやすく 分析しやすいテーブル構造や
< 構成にする手法です 基幹DBは追加されており テーブルの関連が
< 複雑で アナリストが分析しやすい 形式ではありません
< 基幹DBのデータを分析対象の目的別に 再編します
< そして データを分かりやすく 分析しやすいテーブル構造や
< 構成にします 特に分析しやすい テーブル構造や構成というところが
< DWHのデータモデリングの ポイントです
< 仏作って魂入れずということわざが ありますが データモデリングをせずに
< データ分析基盤やDWHという データモデリングの
< 器を導入しただけでは データ分析は図りません
< データモデリングを考えずに導入した データ分析基盤は データマードの乱立
< もしくはデータが分かりにくく 分析に手間がかかるため
< あまり使われなくなってしまったり 分析に適さないテーブル構造や
< 構成による性能低下 オーバープロビジョニングによる
< コストの上昇などの問題が考えられます
< データ分析に重要なDWHの データモデリングは
< データモデリングですが 分かりにくい その理由を私の経験でまとめますと
< 一つ目は データモデリングという 一般的には機関システムで用いられる
< ERモデリング 第三正規化を意味します
< 二つ目は DWHの誕生と その発展に貢献した
< 二人のアーキティフトの 意見の相違がありました
---
> 本セッションは 下記のセッションの続編となりますので
> 併せてご覧いただくことをお勧めします
> 分かりそうで分からない 本日のテーマは
> DWHのデータモデリングです
> DWHのデータモデリングとは何か
> 代表的なデータモデリング手法の紹介と
> 長所 短所 選択のポイントについて解説します
> ここではDWHのデータモデリングの概要と 目的について解説します
> DWHとは何かを理解し
> DWHの目的を確認したいと思います
> データ分析用途のDBであるDWH データウェアハウスに
> データを分かりやすく分析しやすい テーブル構造や構成にする手法です
> 機関DBは追加されており テーブルの関連が複雑で
> アナリストが分析しやすい形式ではありません
> 機関DBのデータを分析対象の 目的別に再編します
> そして データを分かりやすく分析しやすい テーブル構造や構成にします
> 特に分析しやすいテーブル構造や 構成というところが
> DWHのデータモデリングのポイントです
> 仏作って魂入れずという言葉がありますが
> データモデリングをせずに データ分析基盤やDWHという器を
> 導入しただけでは データ分析は図りません
> データモデリングを考えずに 導入したデータ分析基盤は
> データマードの乱立 もしくはデータが分かりにくく
> 分析に手間がかかるため あまり使われなくなってしまったり
> 分析に適さないテーブル構造や 構成による性能低下
> オーバープロビジョニングによる コストの上昇などの問題が考えられます
> データ分析に重要なDWHのデータモデリングですが
> 分かりにくい その理由を 私の経験でまとめますと
> 1つ目は データモデリングという 一般的には
> 機関システムで用いられる ERモデリング 第三成果を意味します
> 2つ目はDWHの誕生と その発展に貢献した
> 2人のアーキティクトの 意見の相違がありました
45,47c51,54
< DWH製品やサービスなどに特化した 情報ばかりが多く
< データの中身についてフォーカスされることが 多くありませんでした
< この三つの情報が 時系列に関係なく 混在しているため
---
> DWH製品やサービスなどに 特化した情報ばかりが多く
> データの中身について フォーカスされることが
> 多くありませんでした
> この3つの情報が 時系列に関係なく 混在しているため
50,55c57,63
< ビル・インモンさんが提唱した
< DWH データウェアハウスという データモデリングと
< そして 最後に最近よく聞く データモデリング手法である
< DataVault 2.0について ご紹介します
< ここでは ビル・インモンさんの DWHのデータモデリングについて解説します
< ビル・インモンさんが提唱する Enterprise DWH データウェアハウスは
---
> ビル・インモンさんが提唱した DWH データウェアハウスという
> データモデリングと そして 最後に最近よく聞く
> データモデリング手法である データボルト2.0について
> ご紹介します
> ここでは ビル・インモンさんの DWHのデータモデリングについて
> 解説します
> ビル・インモンさんが提唱する エンタープライズDWH データウェアハウスは
58,61c66,68
< 統合化は 各部門のデータの表記売れや
< 意味を統一します
< サブジェクト指向は データを目的ごとに分類して
< 分析の軸 次元ごとに集計して 蓄積したデータである
---
> 統合化は 各部門のデータの表記揺れや 意味を統一します
> サブジェクト思考は データを目的ごとに分類して
> 分析の軸・次元ごとに集計して 蓄積したデータである
66,67c73
< DWHは 顧客 製品 ベンダーなど
< 企業が
---
> DWHは顧客・製品・ベンダーなど 企業が

最後に

Whisper Large V3 Turboは、Whisper Large V3と比較して約3.16倍高速に動作しました。これは、デコーダー層の削減による効果が顕著に表れています。

Whisper Large V3 Turboモデルは、処理速度と精度のバランスを優れた形で実現しています。特に、高速化による恩恵は大きく、リアルタイム音声認識や大量の音声データ処理など、幅広いアプリケーションでの活用が期待できます。

一方で、わずかな精度の低下は存在するため、極めて高い精度が要求される特定のユースケースでは、従来のWhisper Large V3モデルの使用を検討する必要があるかもしれません。

本来、Whisperの使用にはGPUが推奨されますが、お手持ちのCPUでも300秒の動画を94秒で文字起こしできるという結果は、非常に魅力的と言えるでしょう。今回の検証では手持ちのMacBook(M1)を使用しましたが、Intel CPUのプラットフォームで検証する場合は--platform linux/arm64の指定は不要です。Windows環境の場合、Dockerを使用せずにWSL2上のUbuntuなどにPythonのパッケージをインストールする方法も選択肢の一つとなります。

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.